#pragma once

#include <vector>
#include <xml/node.h>

namespace xml {

class stream;

template <class T> class manipulator : public T {
public:
	manipulator(const T & t)
		: T(t) { }
	void operator () (stream & s)
		{ T::operator () (s); }
};

template <class T> manipulator<T> manip(const T & t)
	{ return manipulator<T>(t); }

class stream {
private:
	node	_root;
	bool	_in;
public:
	stream(const const_node & n)
		: _root(n), _in(true) { }
	stream(const node & n, bool in)
		: _root(n), _in(in) { }
	stream(const stream & s)
		: _root(s._root), _in(s._in) { }
	~stream()
		{ } 
public:
	const node & root() const
		{ return _root; }
	node & root()
		{ return _root; }
	bool in() const
		{ return _in; }
	bool out() const
		{ return !_in; }
	void root(node n)
		{ _root = n; }
	stream child(string const & tag) {
		if (in())
			return stream(_root.child(tag, node()), true);
		else
			return stream(_root.create(tag), false);
	}
};

typedef stream istream;
typedef stream ostream;

inline stream in_stream(const const_node & n)
	{ return stream(n); }

inline stream out_stream(const node & n)
	{ return stream(n, false); }

//

template <typename T>	
stream & operator || (stream & s, manipulator<T> & m)
{
	m(s);
	return s;
}

template <class T> class xattr;
template <class T> class xdef_attr;
template <class T> class xdef_in_attr;

template <class T> 
manipulator< xattr<T> > attr(const string & name, T & value);
///if attribute's value is absent during reading, it is replaced with the default value;
///if attribute's value coincides with its default value during writing, then no
///write is really performed
template <class T> 
manipulator< xdef_attr<T> > def_attr(const string & name, T & value, const T & def = T());
///acts as attr(...) on writing (that is always writes the value) and
///acts as def_attr(...) on reading (that is substitutes not found attribute with its default value)
template <class T> 
manipulator< xdef_in_attr<T> > def_in_attr(const string & name, T & value, const T & def = T());

template <class T> class xchild {
private:
	const string &	tag;
	T &				value;
public:
	xchild(const string & t, T & v)
		: tag(t), value(v) { }
	void operator () (stream & s)
	{
		node n = s.root();
		if (s.in())
			s.root(n.child(tag, node()));
		else
			s.root(n.create(tag));
		s || value;
		s.root(n);
	}
};

template <class T>
manipulator< xchild<T> > child(const string & tag, T & value)
	{ return manip(xchild<T>(tag, value)); }

///same as xdef_attr but for child nodes
template <class T, class Eq> 
class xdef_child {
private:
	const string &	tag;
	T &				value;
	const T &		def_value;
	Eq				eq;
public:
	xdef_child(const string & t, T & v, const T & d, Eq e)
		: tag(t), value(v), def_value(d), eq(e) { }
	void operator () (stream & s)
	{
		node n = s.root();
		if (s.in()) {
			node c = n.child(tag, node());
			if (c.null()) {
				value = def_value;
				return;
			} 
			else
				s.root(c);
		} else {
			if (eq(value, def_value))
				return;
			s.root(n.create(tag));
		}
		s || value;
		s.root(n);
	}
};

///on input returns default value if the stream has no tagged child,
///on output writes the child only if its value is distinct from default one
template <class T>
inline manipulator< xdef_child<T, std::equal_to<T> > > 
	def_child(const string & tag, T & value, const T & def = T())
{ 
	return manip(xdef_child<T, std::equal_to<T> >(tag, value, def, std::equal_to<T>())); 
}

struct always_false {
	template <class T>
	bool operator () (const T &, const T &)
		{ return false; }
};

///on input returns default value if the stream has no tagged child,
///on output writes the child always
template <class T>
inline manipulator< xdef_child<T, always_false> > 
	def_read_child(const string & tag, T & value, const T & def = T())
{ 
	return manip(xdef_child<T, always_false>(tag, value, def, always_false())); 
}

template <class T>
struct child_locator {
	template <class I> 
	T & in(const I & it)
		{ return *it; }
	template <class I> 
	T & out(const I & it)
		{ return *it; }
};

template <class T>
struct child_locator_ptr {
	template <class I> 
	T & in(const I & it)
		{ return *(*it = new T); }
	template <class I> 
	T & out(const I & it) 
		{ return **it; }
};

template <class C, class L> 
class xchildren {
private:
	const string &	tag;
	C &				container;
	L &				locator;
public:
	xchildren(const string & t, C & c, L & l)
		: tag(t), container(c), locator(l) { }
	void operator () (stream & s)
	{
		if (s.in())	{
			const_node_list list;
			s.root().children(list, tag);
			container.resize(list.size());
			const_node_list::const_iterator n;
			C::iterator i = container.begin();
			for (n = list.begin(); n != list.end(); ++n, ++i)
				stream(*n) || locator.in(i);
		}
		else {
			C::iterator i;
			for (i = container.begin(); i != container.end(); ++i)
				s || child(tag, locator.out(i));
		}
	}
};

template <class C>
inline manipulator< xchildren<C, child_locator<typename C::value_type> > >
children(const string & tag, C & container)
{ 
	return manip(xchildren<C, child_locator<typename C::value_type> >
		(tag, container, child_locator<typename C::value_type>())); 
}

template <class T>
inline manipulator< xchildren<std::vector<T*>, child_locator_ptr<T> > > 
children_ptr(const string & tag, std::vector<T*> & container)
{ 
	return manip(xchildren<std::vector<T*>, child_locator_ptr<T> >
		(tag, container, child_locator_ptr<T>())); 
}

#include "stream.inl"

} // xml

